In [282]:
import numpy as np
import pandas as pd
import holoviews as hv
hv.extension('bokeh', 'matplotlib')
In [283]:
import re
import sys
import itertools

Importar dataset

In [284]:
df = pd.read_csv('hackaturing_3.dsv', index_col=0, dtype={'cid':object})
df.head()
Out[284]:
cnpj prestador uf id_beneficiario sexo data_nascimento id_conta cid crm_solicitante cbos_solicitante ... tipo_item carater_atendimento servico descricao_despesa quantidade valor_item valor_cobrado valor_pago ano_mes age
0 1deff610d3afbb1bc2403d03bb1e53ca f3f4b65055a7f48e36488ac636ba9b5a SP ecdc61d19fd126067e5395f57655796f F 1936-07-05 d2b1252a15812fca5d1d979c7acbb27d NaN d41d8cd98f00b204e9800998ecf8427e NaN ... MEDICAMENTOS ELETIVO 55851.0 PROBIATOP 1G LACTOPRO PO 2.0 5.9840 5.9840 5.9840 201610 82.0
1 1deff610d3afbb1bc2403d03bb1e53ca f3f4b65055a7f48e36488ac636ba9b5a SP 474dbcdd82fb709261c643f8aeb328f8 M 1963-03-04 17c29be731d33ede20056ed4c8b4e5ec NaN d41d8cd98f00b204e9800998ecf8427e NaN ... MEDICAMENTOS ELETIVO 61005.0 SORO FISIOLOGICO 100M 1.0 6.1030 0.6035 0.6035 201610 55.0
2 1deff610d3afbb1bc2403d03bb1e53ca f3f4b65055a7f48e36488ac636ba9b5a SP 8bfeb1e7e1eb30f828ed305da99a5879 F 1945-01-15 adefa9bd776553890d37d8ebb0462340 NaN d41d8cd98f00b204e9800998ecf8427e NaN ... MATERIAIS ELETIVO 256200.0 HIPOCLORITO SODIO POR ML 100.0 0.8500 0.8500 0.8500 201610 73.0
3 1deff610d3afbb1bc2403d03bb1e53ca f3f4b65055a7f48e36488ac636ba9b5a SP 0de0de46f846e2ed0ee5e03ae17003c5 F 1963-08-22 cb8915f145f01ad43d6b28a10544566b NaN d41d8cd98f00b204e9800998ecf8427e NaN ... MATERIAIS ELETIVO 280022.0 ARNICA GEL 30G TB 1.0 15.5805 15.5805 15.5805 201610 55.0
4 1deff610d3afbb1bc2403d03bb1e53ca f3f4b65055a7f48e36488ac636ba9b5a SP 4b68daa346363196bed3e70bb20a869f M 1957-01-18 3e870dd98a8e673e586bc3a63c77e767 NaN d41d8cd98f00b204e9800998ecf8427e NaN ... MEDICAMENTOS ELETIVO 61005.0 SORO FISIOLOGICO 100M 1.0 6.1030 0.6035 0.6035 201610 61.0

5 rows × 26 columns

Ajustes nos dados

In [285]:
df['valor_glosa'] = df['valor_cobrado'] - df['valor_pago']
In [286]:
print('Total de glosas: R$ {:,.0f}'.format(df['valor_glosa'].sum()))
Total de glosas: R$ 509,304

Distribuição de glosas

In [287]:
%%opts Histogram [width=700, show_grid=True]
glosas = np.array(df[df['valor_glosa']>0]['valor_glosa'])
densities, values = np.histogram(glosas, bins=100, density=True, weights=glosas)
cumulative = np.cumsum(densities) * (values[1]-values[0])
hv.Histogram((cumulative, values), label='Distribuição das glosas em R$').redim(
    x={'label':'Valor de Cada Glosa Individual'}, 
    Frequency={'range':(0,1), 'label':'% do Total de Glosas'}
)
Out[287]:
In [288]:
%%opts Histogram [width=700, show_grid=True, tools=['hover']]
glosas = np.array(df[df['valor_glosa']>0]['valor_glosa'])
densities, values = np.histogram(glosas, bins=100000, density=True, weights=glosas)
cumulative = np.cumsum(densities) * (values[1]-values[0])
hv.Histogram((cumulative, values), label='Distribuição das glosas em R$ (detalhe das glosas menores)').redim(
    x={'range':(0,10), 'label':'Valor de Cada Glosa Individual'}, 
    Frequency={'range':(0,0.1), 'label':'% do Total de Glosas'}
)
Out[288]:
In [289]:
%%opts Histogram [width=700, show_grid=True, tools=['hover']]
glosas = np.array(df[df['valor_glosa']>0]['valor_glosa'])
densities, values = np.histogram(glosas, bins=100000, density=True)
cumulative = np.cumsum(densities) * (values[1]-values[0])
hv.Histogram((cumulative, values), label='Distribuição das glosas em número (detalhe das glosas menores)').redim(
    x={'range':(0,10), 'label':'Valor de Cada Glosa Individual'}, 
    Frequency={'range':(0,1), 'label':'% do # Total de Glosas'}
)
Out[289]:

Nota:

As glosas de até 3 reais representam 55% do número total de glosas mas, somente, 1.3% do valor total.
Então, faz sentido abrir mão de todo o processamento de glosas menores?
50% a menos do trabalho por um custo de 1.3%, mas que vai direto no bottom-line?
Se sim, talvez, então, valha a mesma conta para glosas de valores indivudiais maiores também... a se estudar com as operadoras

Verificar valores para glosas muito pequenas (<1)

In [290]:
%%opts Table [width=2000]
hv.Table(df[(df['valor_glosa']<1)&(df['valor_glosa']>0)][[
    'sexo', 'data_nascimento', 'cid', 'data_entrada', 'tipo_guia', 'tipo_item', 'carater_atendimento', 'servico', 
    'descricao_despesa', 'quantidade', 'valor_item', 'valor_cobrado', 'valor_pago', 'ano_mes', 'age', 'valor_glosa'
]])
Out[290]:

Definição de Glosa:

valor glosado > R$ 0.01
valor glosado > 1% do valor cobrado

In [301]:
df['glosa'] = (df['valor_glosa']>0.01) & (df['valor_glosa']>0.01*df['valor_cobrado']).astype(int)
Deletar valores anomalos
In [300]:
df = df.drop(df[df['valor_glosa']<0].index)
Verificar qual % o valor de glosa definido representa da glosa total:
In [302]:
df[df['glosa']==True]['valor_glosa'].sum() / df['valor_glosa'].sum()
Out[302]:
0.9999252186063842
Total de valores com glosa (de acordo com a definição), com qualquer glosa, e total:
In [303]:
df[df['glosa']].shape[0], df[df['valor_glosa']>0].shape[0], df.shape[0]
Out[303]:
(12312, 12590, 661023)

Datas, datas e datas...

In [319]:
df['data_item'] = pd.to_datetime(df['data_item'])
df['data_entrada'] = pd.to_datetime(df['data_entrada'])
df['data_saida'] = pd.to_datetime(df['data_saida'])
df['data_nascimento'] = pd.to_datetime(df['data_nascimento'])
df['dias_tratamento'] = (df['data_saida'] - df['data_entrada']).dt.days
df['weekday_entrada'] = df['data_entrada'].dt.dayofweek
df['weekday_item'] = df['data_item'].dt.dayofweek
df['mes'] = df['data_item'].dt.month

Comportamento das variáveis que influenciam a glosa

Categóricas

In [313]:
%%opts Bars [height=300 width=350]
hv.Bars(df.groupby('sexo')['glosa'].mean()) + hv.Bars(df.groupby('tipo_guia')['glosa'].mean())
Out[313]:
In [320]:
%%opts Bars [height=300 width=350]
hv.Bars(df.groupby('weekday_entrada')['glosa'].mean()) + hv.Bars(df.groupby('weekday_item')['glosa'].mean())
Out[320]:
In [317]:
%%opts Bars [height=300 width=350 xrotation=30]
hv.Bars(df.groupby('tipo_item')['glosa'].mean())
Out[317]:
In [318]:
%%opts Bars [height=300 width=350 xrotation=30]
hv.Bars(df.groupby('carater_atendimento')['glosa'].mean())
Out[318]:
In [330]:
%%opts Bars [height=300 width=400 xrotation=30]
hv.Bars(df.groupby('mes')['glosa'].mean())
Out[330]:

Dando uma olhada nos dados por ano_mes, há algum comportamento estranho nos meses antes de 02/2017...
Possivelmente devido ao slicing do dataset.
Idealmente esse estudo deveria ser refeito com um dataset mais completo, e com o timeframe maior.

In [324]:
%%opts Bars [height=300 width=450 xrotation=30]
hv.Bars(df.groupby('prestador')['glosa'].mean())
Out[324]:

Ainda daria para tentar pelo beneficiario final e pela operadora.
Mas, devido a alta cardinalidade do beneficiario nesse dataset, possivelmente, não trará algum resultado significativo (a não ser que overfit)

Numéricas

In [ ]:
 
In [ ]:
 
In [ ]: